// 防抖与节流

/* 发现如果在vue里面写参数,是没有类型提醒的,那还是补全吧,小恶心 */

import { GeneralFunction } from '@/bean/base';

// 节流
const v_throttle = function (func: GeneralFunction, wait: number) {
  // 首先先判断是否是函数吧,不是函数还说个der
  if (typeof func !== 'function')
    throw new TypeError('func must be a function!');
  // 这波是强行转换一下数字型的,防止你乱搞,但是你上了ts就无所谓的
  wait = +wait;
  if (isNaN(wait)) wait = 300;

  let timer: null | number = null,
    previous = 0,
    result: unknown;
  // 执行传递的函数
  return function proxy(this: HTMLElement, ...params: []) {
    let now = +new Date(),
      remaining = wait - (now - previous),
      self = this;

    // 由于now是时间戳,所以第一次执行肯定是负数
    // console.log('第一次进来这里');
    if (remaining <= 0) {
      //但其实这玩意根本不会存在,但是为了代码的健壮性,顺手加上
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      // 上一次的时间
      previous = now;
      result = func.apply(self, params);
      return result;
    }
    // 在节流时间内再次触发事件就进来这里
    if (!timer) {
      timer = window.setTimeout(() => {
        window.clearTimeout(timer as number);
        timer = null;
        previous = +new Date();
        result = func.apply(self, params);
      }, remaining);
    }

    return result;
  };
};

// 防抖
const v_debounce = function (
  func: GeneralFunction,
  wait: number,
  immediate: boolean
) {
  if (typeof func !== 'function')
    throw new TypeError('func must be a function!');
  if (typeof wait === 'undefined') {
    wait = 500;
    immediate = false;
  }
  if (typeof wait === 'boolean') {
    immediate = wait;
    wait = 500;
  }
  if (typeof immediate === 'undefined') {
    immediate = false;
  }
  if (typeof wait !== 'number') throw new TypeError('wait must be a number!');
  if (typeof immediate !== 'boolean')
    throw new TypeError('immediate must be a boolean!');

  let timer: number | null = null,
    result: unknown;
  return function proxy(this: HTMLElement, ...params: []) {
    let self = this,
      callNow = !timer && immediate;
    if (timer) clearTimeout(timer);
    timer = window.setTimeout(() => {
      clearTimeout(timer as number);
      timer = null;
      // 当第二次触发时,如果是立即
      if (!immediate) result = func.apply(self, params);
    }, wait);
    // 第一次触发在这里
    if (callNow) result = func.apply(self, params);
    return result;
  };
};

interface throttleOrdebounce {
  value: {
    func: GeneralFunction<unknown>;
    wait?: number;
    immediate?: boolean;
    params?: [];
    type: string;
  };
}

interface throttleOrdebounceElement extends HTMLElement {
  $type: string;
  $handle: GeneralFunction<unknown>;
}

export const debounce = {
  beforeMount(el: throttleOrdebounceElement, binding: throttleOrdebounce) {
    let {
      func,
      wait = 300,
      immediate = true,
      params = [],
      type = 'click',
    } = binding.value;

    const handle = v_debounce,
      proxy = () => {
        return func.call(this, ...params);
      };
    el.$type = type;
    el.$handle = handle(proxy, wait, immediate);
    el.addEventListener(el.$type, el.$handle);
  },
  unmounted(el: throttleOrdebounceElement): void {
    el.removeEventListener(el.$type, el.$handle);
  },
};

export const throttle = {
  beforeMount(el: throttleOrdebounceElement, binding: throttleOrdebounce) {
    let { func, wait = 300, params = [], type = 'click' } = binding.value;
    const proxy: GeneralFunction<unknown> = () => {
      return func.call(this, ...params);
    };
    el.$type = type;
    el.$handle = v_throttle(proxy, wait);
    el.addEventListener(el.$type, el.$handle);
  },
  unmounted(el: throttleOrdebounceElement): void {
    el.removeEventListener(el.$type, el.$handle);
  },
};
